package com.jdd.modules.sdk;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jdd.common.api.vo.Result;
import com.jdd.common.api.vo.ResultVo;
import com.jdd.common.aspect.annotation.AutoLog;
import com.jdd.common.constant.JddConstant;
import com.jdd.common.exception.JddException;
import com.jdd.common.util.*;
import com.jdd.config.websocket.WebSocketServer;
import com.jdd.modules.operationLog.entity.ParkHandOverLog;
import com.jdd.modules.operationLog.mapper.ParkHandOverLogMapper;
import com.jdd.modules.operationLog.service.IParkHandOverLogService;
import com.jdd.modules.order.entity.ParkOrderInfo;
import com.jdd.modules.order.mapper.ParkOrderInfoMapper;
import com.jdd.modules.order.service.IParkOrderInfoService;
import com.jdd.modules.parkCarAbnormalRecord.service.IParkCarAbnormalRecordService;
import com.jdd.modules.parkcar.service.IParkCarOutAbnormalLogService;
import com.jdd.modules.parkcar.service.IParkSpaceService;
import com.jdd.modules.parking.entity.ConfigParkInfo;
import com.jdd.modules.parking.entity.ParkArmInfo;
import com.jdd.modules.parking.entity.ParkCarEnterLog;
import com.jdd.modules.parking.entity.ParkCarOutLog;
import com.jdd.modules.parking.mapper.ParkArmInfoMapper;
import com.jdd.modules.parking.mapper.ParkCarEnterLogMapper;
import com.jdd.modules.parking.service.*;
import com.jdd.modules.sdkidentifyresultpushlog.entity.ParkOrderInfoVO;
import com.jdd.modules.sdkidentifyresultpushlog.entity.RestTemplateSpaceVo;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

import static com.jdd.common.constant.CacheConstant.CURRENT_EXIT_LICENSE_PLATE_TEN;

/**
 * @version 1.0
 * @author： jdd尚岳
 * @date： 2021-03-26 17:37
 */
@Slf4j
@RestController
@RequestMapping("/sdk")
public class SdkController {
    @Resource
    private IParkCarOutEntranceRecordService parkCarOutEntranceRecordService;
    @Resource
    private IParkArmInfoService parkArmInfoService;
    @Resource
    private ParkArmInfoMapper parkArmInfoMapper;
    @Resource
    private IparkCarEnterLogService carEnterLogService;
    @Resource
    private IparkCarOutLogService carOutLogService;
    @Resource
    private IParkOrderInfoService orderInfoService;
    @Resource
    private WebSocketServer socketServer;
    @Resource
    private SdkCallInterface sdkCallInterface;
    @Resource
    private IConfigParkInfoService configParkInfoService;
    @Resource
    private IParkCarOutAbnormalLogService parkCarOutAbnormalLogService;
    @Resource
    private ParkCarEnterLogMapper parkCarEnterLogMapper;
    @Resource
    private IParkWorkStationService parkWorkStationService;
    @Resource
    private IParkHandOverLogService parkHandOverLogService;
    @Resource
    private ParkOrderInfoMapper parkOrderInfoMapper;
    @Resource
    private ParkHandOverLogMapper parkHandOverLogMapper;
    @Resource
    private CacheUtil cacheUtil;
    @Resource
    private IParkSpaceService parkSpaceService;


    @Resource
    private RestTemplateUtils restTemplateUtils;

    public String appScrect;

    public String appId;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private IParkCarAbnormalRecordService parkCarAbnormalRecordService;

    /**
     * @param ip 设备IP
     * @return
     * @Description 链接设备对设备进行初始化
     * @author shangyue
     * @Date 2021/4/2
     */
    @AutoLog(value = "设备初始化")
    @ApiOperation(value = "设备初始化", notes = "设备初始化-通过设备ip")
    @GetMapping(value = "/init")
    public Result init(@RequestParam("ip") String ip) {
        //先查看是什么类型的相机
        ConfigParkInfo configParkInfo = configParkInfoService.findConfigParkInfoByParkCode();
        if (ObjectUtil.isNull(configParkInfo)) {
            return Result.error("车辆信息配置为空，请检查");
        }
        //通用初始化
        return sdkCallInterface.CameraSdkInit(ip, configParkInfo);
    }

    /**
     * @param serialNo 设备序列号
     * @return
     * @Description 设备IO开闸
     * @author shangyue
     * @Date 2021/4/2
     */
    @AutoLog(value = "设备开闸")
    @ApiOperation(value = "设备开闸", notes = "设备开闸-通过序列号开闸")
    @GetMapping(value = "/unfold")
    public Result sdkUnfold(@RequestParam("serialNo") String serialNo) {
        //处理应急开闸的异常记录
        parkCarAbnormalRecordService.abnormalRecordForceTrigger(serialNo);
        //开闸
        Result result = sdkCallInterface.SdkUnfoldAll(serialNo);
        return result;
    }


    /**
     * @return
     * @Description 设备IO开闸
     * @author shangyue
     * @Date 2021/4/2
     */
    @AutoLog(value = "设备开闸")
    @ApiOperation(value = "设备开闸", notes = "设备开闸-通过序列号开闸")
    @PostMapping(value = "/ioUnfold")
    public Result ioUnfold(@RequestBody ParkOrderInfoVO vo) {
        try {
            ParkCarEnterLog carEnterLog = new ParkCarEnterLog();
            //生成入场纪录类型
            String carEnterId = vo.getCarEnterId();
            String chargeTotalFee = vo.getChargeTotalFee();
            //是否匹配的
            Integer isMatching = null;
            //出场的车牌
            String truePlateNo = vo.getPlateNo();
            String outPlateNo = cacheUtil.get("truePlateNo", truePlateNo);
            //获取车牌号信息
            ParkOrderInfo info = new ParkOrderInfo();
            //走这里的都是线下收费
            vo.setPayType("6");
            //加上工作站名和岗亭人账户
            ParkArmInfo armInfoBySerialNo = parkArmInfoMapper.getParkArmBySerialNo(vo.getSerialNo());
            String workUser = parkWorkStationService.getStationOperationNameByParkArm(armInfoBySerialNo);
            if (ObjectUtil.isNotEmpty(workUser)) {
                vo.setWorkName(armInfoBySerialNo.getWorkStationName());
                vo.setSysUserName(workUser);
            }
            BeanUtils.copyProperties(vo, info);
            info.setId(SnowFlakeUtil.getId());
            info.setPayTime(DateUtil.format(new Date(),DatePattern.NORM_DATETIME_PATTERN));
            info.setOutTime(DateUtil.format(new Date(),DatePattern.NORM_DATETIME_PATTERN));
            info.setCreateTime(DateUtils.getDate());
            if(JddConstant.OrderConstant.ORDER_PAY_STATE_UNPAID.equals(info.getOrderStatus())){
                info.setChargePaid("0");
            }else {
                info.setChargePaid(vo.getChargeUnpaid());
            }
            if(ObjectUtil.isNotNull(info.getVersion())) {
                //后面要插入数据库，所以这个字段单独拿出来
                isMatching = info.getVersion();
                info.setVersion(null);
            }
            ParkCarOutLog carOutLog = new ParkCarOutLog();
            //没有入场信息
            if (StringUtils.isBlank(carEnterId)) {
                BeanUtils.copyProperties(vo, carOutLog);
                carOutLog.setId(SnowFlakeUtil.getId());
                carOutLog.setCarOutTime(DateUtil.format(new Date(),DatePattern.NORM_DATETIME_PATTERN));
                carOutLog.setCarNature(JddConstant.StringNumber.STRING_ZERO);
                carOutLog.setImgPath(vo.getOutImgPath());
                carOutLog.setOssImgPath(vo.getOssOutImgPath());
                carOutLog.setCarType(JddConstant.StringNumber.STRING_ONE);
                carOutLog.setCreateTime(new Date());
                carOutLog.setOuterType(JddConstant.IntNumber.INT_TWO);
                carOutLogService.save(carOutLog);
                //如何订单金额不为0 保存订单信息
                //入场记录不存在，不保存订单金额
                if (ObjectUtil.isNotEmpty(chargeTotalFee)&&!JddConstant.StringNumber.STRING_ZERO.equals(chargeTotalFee)) {
                    //保存订单信息
                    orderInfoService.save(info);
                }
            } else {
                //月租车中途过期，缴费后车位制空操作
                //从入场纪录拿到车位号
                List<ParkCarEnterLog> carEnterLogList = getParkCarEnterLogList(truePlateNo);
                if (CollUtil.isNotEmpty(carEnterLogList)) {
                    ParkCarEnterLog lastCarEnterLog = carEnterLogList.get(0);
                    //以前有入场记录，而且又是月租车，则把原来的入场记录的月租车车位改成未停靠
                    if (StringUtils.isNotBlank(lastCarEnterLog.getParkNumber())) {
                        parkSpaceService.updateParkStatus(lastCarEnterLog, JddConstant.ParkSpace.PARKSTATUS_ZERO);
                        //发送HTTP请求云端同步更新月租车位状态
                        RestTemplateSpaceVo restTemplateSpaceVo = new RestTemplateSpaceVo();
                        restTemplateSpaceVo.setLatestParkCarEnterLogStr(JSONObject.toJSONString(lastCarEnterLog));
                        log.info("发送请求到云端同步更新月租车车位停靠状态:{}", sendCloudSynSpace(restTemplateSpaceVo));
                    }
                }
                //正常开闸
                carEnterLog = carEnterLogService.getById(carEnterId);
                if(JddConstant.ParkSpace.PARKSTATUS_ONE.equals(carEnterLog.getIsOut()))
                {
                    log.error("车辆已离场");
                    throw new JddException(500,"车辆已离场");
                }
                BeanUtils.copyProperties(vo, carOutLog);
                //这里不用赋值carType,用的是入场记录的carType,当是异常车辆时，出场记录的carType为null
                carOutLog.setId(SnowFlakeUtil.getId());
                carOutLog.setCarOutTime(DateUtil.format(new Date(),DatePattern.NORM_DATETIME_PATTERN));
                carOutLog.setImgPath(vo.getOutImgPath());
                carOutLog.setOssImgPath(vo.getOssOutImgPath());
                carOutLog.setCarNature(carEnterLog.getCarNature());
                carOutLog.setOuterType(2);
                carOutLog.setCreateTime(new Date());
                //如果是匹配情况下，描述下识别错误的
                if (null != outPlateNo) {
                    if (vo.getRemark() != null) {
                        carOutLog.setRemark("出场时识别为" + outPlateNo + vo.getRemark());
                    } else {
                        carOutLog.setRemark("出场时识别为" + outPlateNo);
                    }
                }

                //补录
                if (carEnterLog.getSerialNo().equals(vo.getSerialNo())) {
                    carOutLog.setOuterType(JddConstant.IntNumber.INT_THREE);
                    if (vo.getRemark() != null) {
                        carOutLog.setRemark("补录场内" + vo.getRemark());
                    } else {
                        carOutLog.setRemark("补录场内");
                    }
                }

                //保存出场信息
                carOutLogService.save(carOutLog);
                //不用管识别的车牌，因为识别的车牌没有入场纪录
                parkCarEnterLogMapper.updateIsOut(carEnterLog.getPlateNo());
                //将十分钟内的异常记录删除
                parkCarAbnormalRecordService.deletedLastestAbnormalRecord(carOutLog.getParkCode(),carOutLog.getPlateNo(),carOutLog.getSerialNo());
                //保存订单信息
                orderInfoService.save(info);
                parkCarOutEntranceRecordService.assemblyCarOutEntranceRecord(carOutLog);
            }
            //监控左上角显示的数据
            //JSONObject successOrder = new JSONObject();
           // successOrder.put("successOrder", info);
            //发送到监控的数据
            ParkArmInfo parkArmInfoByIp = parkArmInfoMapper.getParkArmBySerialNo(info.getSerialNo());
         //   socketServer.sendInfo(successOrder.toJSONString(), parkArmInfoByIp.getUsedUserId());
            //正常情况，入场纪录id不为空
            if (ObjectUtil.isNotEmpty(carEnterLog) && ObjectUtil.isNotEmpty(carEnterLog.getId())) {
                //订单vo的版本号没有数据，入场序列号与识别闸机序列号不同，入场记录车牌号与当前识别的车牌相同
                if(ObjectUtil.isNull(vo.getVersion())&&!vo.getSerialNo().equals(carEnterLog.getSerialNo())&&vo.getPlateNo().equals(carEnterLog.getPlateNo())){
                    //计算非临时车用
                    carOutLog.setCarNature(JddConstant.StringNumber.STRING_ZERO);
                    //这里更新交接班数据（正常情况下的）
                    updateHandLog(carEnterLog, info, workUser, carOutLog, parkArmInfoByIp);
                    cacheUtil.put(CURRENT_EXIT_LICENSE_PLATE_TEN, parkArmInfoByIp.getSerialNo(), vo.getPlateNo());
                    //如果正常就提前返回
                    return sdkCallInterface.SdkUnfoldAll(JddConstant.SdkOpenGateLog.SWICH_TYPE_ZERO, vo.getSerialNo(), vo.getPlateNo());
                }
            }
            //如果是补录，入场纪录序列号与当前序列号相同
            //如果是匹配的，version字段有值
            //这里存入异常记录
            //是否匹配的字段加上
            info.setVersion(isMatching);
            parkCarOutAbnormalLogService.saveAbnormal(carEnterLog, info);
            //交接班数据更新（异常情况的）同时更新至前端
            if (ObjectUtil.isNotEmpty(workUser)) {
                //记录数据，不插入数据库，把支付状态改为已支付
//                info.setOrderStatus(JddConstant.OrderConstant.ORDER_PAY_STATE_SUCCESS);
                //交接班更新
                int abnormal = 0;
                if (null == isMatching) {
                    abnormal = 1;
                }
                ParkHandOverLog parkHandOverLog = parkHandOverLogService.updateHandOver(info, workUser, abnormal);
                JSONObject HandOverLog = new JSONObject();
                HandOverLog.put("HandOverLog", parkHandOverLog);
                socketServer.sendInfo(HandOverLog.toJSONString(), parkArmInfoByIp.getUsedUserId());
                //传送前端通知关闭对话框
                Map<String, Object> data = parkArmInfoService.getData(workUser);
                socketServer.sendInfo(JSON.toJSONString(data), parkArmInfoByIp.getUsedUserId());
                //放到缓存,处理收费后重复识别问题
                cacheUtil.put(CURRENT_EXIT_LICENSE_PLATE_TEN, parkArmInfoByIp.getSerialNo(), vo.getPlateNo());
            }
            //走到这步，说明不是补录就是匹配
            return sdkCallInterface.SdkUnfoldAll(null == isMatching ? JddConstant.SdkOpenGateLog.SWICH_TYPE_CREATECARINTER : JddConstant.SdkOpenGateLog.SWICH_TYPE_MATCING, vo.getSerialNo(), vo.getPlateNo());
        } catch (Exception e) {
            log.error("手动开闸异常信息：{}",e);
            if(e.getClass().equals(JddException.class))
            {
                return Result.error(e.getMessage());
            }
            return Result.error("手动出场异常");
        }

    }

    private void updateHandLog(ParkCarEnterLog carEnterLog, ParkOrderInfo info, String workUser, ParkCarOutLog carOutLog, ParkArmInfo parkArmInfoByIp) throws IOException {
        //交接班数据更新同时更新至前端
        if (ObjectUtil.isNotEmpty(workUser)) {
            BigDecimal chargePaid = new BigDecimal(0);
            BigDecimal couponAmt = new BigDecimal(0);
            BigDecimal chargeDue = new BigDecimal(0);
            // 判断是否还有场内码的订单
            // 归纳统计订单
            List<ParkOrderInfo> orderByCarEnterIds = parkOrderInfoMapper.getOrderByCarEnterId(carEnterLog.getId());
            if(ObjectUtil.isNotEmpty(orderByCarEnterIds)){

                for (ParkOrderInfo orderByCarEnterId : orderByCarEnterIds) {
                    //线上收费且支付状态为成功的
                    if (!JddConstant.StringNumber.STRING_SIX.equals(orderByCarEnterId.getPayType()) && JddConstant.OrderConstant.ORDER_PAY_STATE_SUCCESS.equals(orderByCarEnterId.getOrderStatus())) {
                        chargePaid = chargePaid.add(new BigDecimal(null == orderByCarEnterId.getChargePaid() ? "0" : orderByCarEnterId.getChargePaid()));
                        couponAmt = couponAmt.add(new BigDecimal(null == orderByCarEnterId.getCouponAmt() ? "0" : orderByCarEnterId.getCouponAmt()));
                        chargeDue = chargePaid.add(couponAmt);
                        //场内码的算给谁
                        orderByCarEnterId.setSysUserName(workUser);
                        parkOrderInfoMapper.updateById(orderByCarEnterId);
                    }
                }
                //如果场内码产生费用了再更新，没产生费用不更新
                if (!"0".equals(chargePaid.toString()) || !"0".equals(couponAmt.toString()) || !"0".equals(chargeDue.toString())) {
                    chargePaid = chargePaid.divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
                    chargeDue = chargeDue.divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
                    couponAmt = couponAmt.divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP);
                    //场内码更新
                    ParkHandOverLog hand = parkHandOverLogMapper.getHand(workUser);
                    hand.setOnlineReceivable(hand.getOnlineReceivable().add(chargeDue));
                    hand.setOnlinePaid(hand.getOnlinePaid().add(chargePaid));
                    hand.setOnlineFree(hand.getOnlineFree().add(couponAmt));
                    parkHandOverLogMapper.updateById(hand);
                    info.setOrderStatusDesc(JddConstant.OrderConstant.ORDER_PAY_STATE_SUCCESS);
                }
            }
            parkHandOverLogService.updateHandOver(info, workUser, JddConstant.IntNumber.INT_ZERO);
            Map<String, Object> data = parkArmInfoService.getData(workUser);
            socketServer.sendInfo(JSONObject.toJSONString(data), parkArmInfoByIp.getUsedUserId());
        }
    }

    /**
     * 入场记录
     *
     * @param plateNo 车牌号
     * @return
     */
    private List<ParkCarEnterLog> getParkCarEnterLogList(String plateNo) {
        QueryWrapper<ParkCarEnterLog> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("plate_no", plateNo);
        queryWrapper.eq("is_out", JddConstant.StringNumber.STRING_ZERO);
        queryWrapper.orderByDesc("car_enter_time");
        return carEnterLogService.list(queryWrapper);
    }

    /**
     * 发送请求云端同步月租车位状态
     *
     * @param restTemplateSpaceVo
     * @return com.jdd.common.api.vo.ResultVo
     * @author jdd孙庆伟
     * @date 2021/10/15 14:15:05
     * @version 1.0
     */
    public ResultVo sendCloudSynSpace(RestTemplateSpaceVo restTemplateSpaceVo) {
        String url = "";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        //封装参数
        MultiValueMap<String, String> stringStringSortedMap = EncapsulationSignDataUtils.encapsulationParameters(restTemplateSpaceVo, appId, appScrect);
        // 组装请求体
        HttpEntity<MultiValueMap<String, String>> request =
                new HttpEntity<MultiValueMap<String, String>>(stringStringSortedMap, headers);
        ResponseEntity<ResultVo> cmsResultDTO = restTemplateUtils.post(url, request, ResultVo.class);
        return cmsResultDTO.getBody();
    }

    /**
     * @param serialNo 设备序列号
     * @return
     * @Description 用户可以周期调用该函数来主动查询设备是否断线
     * @author shangyue
     * @Date 2021/4/2
     */
    @AutoLog(value = "查询设备是否在线")
    @ApiOperation(value = "查询设备是否在线", notes = "查询设备是否在线-通过序列号查询")
    @GetMapping(value = "/sdkGetIsConnect")
    public Result sdkGetIsConnect(@RequestParam("serialNo") String serialNo) {
        return sdkCallInterface.IsConnectAll(serialNo);
    }

 }
